<html>
<head>
<meta charset="utf-8">
<meta name="author" content="Marcin Ulikowski">
<meta name="description" content="Find lookalike phishing domains that adversaries can use to attack you">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>dnstwist | phishing domain scanner</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<style>
html {
	overflow-y: scroll;
}
body {
	font-family: -apple-system,Segoe UI,Ubuntu,Helvetica,Arial;
	background-color: #fafbfc;
	color: #353739;
	margin: 0;
}
div#container {
	position: relative;
	width: 70%;
	margin: 2em auto 0 auto;
	}
table {
	border-collapse: collapse;
	width: 100%;
	margin: 3em 0;
	table-layout: fixed;
}
tr {
	border-bottom: 1px solid #ddd;
}
th {
	padding: 5px 0;
	text-align: left;
	font-weight: 600;
}
td {
	padding: 5px 0;
	vertical-align: top;
	word-wrap: break-word;
}
header {
	text-align: center;
}
header h1 {
	margin: 0;
}
header p {
	color: #ef006f;
	font: bold 150% Tahoma,Verdana;
	margin: 0;
	width: 75%;
	float: right;
}
div#searchbox {
	display: flex;
	margin: 4em auto 0 auto;
}
input#url {
	font-size: 1.1em;
	height: 2.8em;
	border: 1px solid #ccc;
	border-radius: 5px 0 0 5px;
	padding-left: .75em;
	width: 80%;
	outline: none;
}
button#scan {
	color: white;
	background-color: #3490dc;
	border: 0;
	border-radius: 0 5px 5px 0;
	height: 2.8em;
	font-size: 1.1em;
	font-weight: 600;
	flex-grow: 1;
	outline: none;
}
button#scan:hover {
	background-color: #2779bd;
}
sup {
	color: #777;
}
progress {
	-webkit-appearance: none;
	-moz-appearance: none;
	height: 5px;
	width: 100%;
	background-color: transparent;
	border: 0;
	margin: 5px 0;
}
progress::-webkit-progress-bar {
	background-color: transparent;
}
progress::-webkit-progress-value {
	background-color: #3490dc;
}
progress::-moz-progress-bar {
	background-color: #3490dc;
}
span#status {
	float: right;
	font-size: 80%;
}
a {
	color: inherit;
	text-decoration: underline;
	text-decoration-skip: ink;
}
a:hover {
	text-decoration: none;
}
a#link {
	color: inherit;
	text-decoration: none;
}
span#source {
	position: fixed;
	right: 1em;
	bottom: 1em;
	background-color: #fafbfc;
}
@media (max-width: 800px) {
	body, table, span {
		font-size: 75%;
	}
	div#container {
		width: 95%;
	}
	img#logo {
		width: 80%;
	}
}
</style>
</head>
<body>
	<div id="container">
		<div>
			<header>
				<h1><img id="logo" src=""></h1>
				<p>phishing domain scanner</p>
			</header>
			<div id="searchbox">
				<input type="hidden" id="sid">
				<input type="text" id="url" placeholder="Enter domain name" autofocus>
				<button id="scan">Scan</button>
			</div>
			<progress id="progress" value="0"></progress>
			<span id="status"></span>
		</div>
		<table id="data"></table>
	</div>
	<span id="source">[<a href="https://github.com/elceef/dnstwist">source code</a>]</span>
</body>
<script>
last_registered = 0;

function fetchDomains() {
	$.getJSON('/api/scans/' + $('#sid').val() + '/domains', function(data) {
		$('#data').empty();
		$('<tr>').html(
				'<th>PERMUTATION</th>' +
				'<th>IP ADDRESS</th>' +
				'<th>NAME SERVER</th>' +
				'<th>MAIL SERVER</th>'
			).appendTo('#data');
		$.each(data, function(i, item) {
			fuzzer = item['fuzzer'] || ''
			permutation = item['domain'] || ''
			ipaddr = [
				(item['dns_a'] || [''])[0].replace('!ServFail', '🚫'),
				(item['dns_aaaa'] || [''])[0].replace('!ServFail', '')
			].filter(Boolean).join('</br>');
			dns_ns = (item['dns_ns'] || [''])[0].replace('!ServFail', '🚫');
			dns_mx = (item['dns_mx'] || [''])[0].replace('!ServFail', '🚫');
			geoip = item['geoip'] || '';
			$('<tr>').html(
				'<td>' + permutation + ' <a href="http://' + permutation + '" id="link">🔗</a></br><sup>' + fuzzer + '</sup></td>' +
				'<td>' + ipaddr + '</br><sup>' + geoip + '</sup></td>' +
				'<td>' + dns_ns + '</td>' +
				'<td>' + dns_mx + '</td>'
			).appendTo('#data');
		});
	});
}

function pollScan() {
    $.getJSON('/api/scans/' + $('#sid').val(), function(data) {
        $('#status').html('Processed ' + data['complete'] + ' of ' + data['total']);
        $('#progress').val(data['complete']/data['total']);
		if (data['remaining'] > 0) {
			setTimeout(pollScan, 250);
		} else {
			sid = $('#sid').val()
			$('#status').html('Scanned <a href="/api/scans/' + sid + '/list">' + data['complete'] + '</a> suspicious domains. Identified ' + data['registered'] + ' registered: download as <a href="/api/scans/' + sid + '/csv">CSV</a> or <a href="/api/scans/' + sid + '/json">JSON</a>');
			$('#scan').text('Scan');
		}
		if (last_registered < data['registered']) {
			last_registered = data['registered']
			fetchDomains();
		}
    });
}

function actionScan() {
	if (!$('#url').val()) {
		$('#status').html('↖ You need to type in a domain name first');
		return
	}

	if ($('#scan').text() == 'Scan') {
		last_registered = 0;
		$('#scan').text('⏱');
		$.post({
			url: '/api/scans',
			data: JSON.stringify({'url': $('#url').val()}),
			contentType: 'application/json',
			success: function(data) {
				$('#sid').val(data['id']);
				$('#scan').text('Stop');
				pollScan();
			},
			error: function(xhr, status, error) {
				$('#scan').text('Scan');
				$('#status').html(xhr.responseJSON['message'] || 'Something went wrong');
			},
		});
	} else {
		stop();
		$.post({
			url: '/api/scans/' + $('#sid').val() + '/stop',
			contentType: 'application/json',
			success: function() {
				$('#scan').text('Scan');
			}
		});
	}
}

$('#scan').click(function() {
	actionScan();
});

$('#url').on('keypress',function(e) {
    if(e.which == 13) {
        actionScan();
    }
});
</script>
</html>
